#include "document.h"

/** 
 * This file is part of uhferret.
 *
 * Author::    Peter Lane
 * Copyright:: Copyright 2011, Peter Lane.
 * License::   GPLv3
 *
 * uhferret is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * uhferret is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with uhferret.  If not, see <http://www.gnu.org/licenses/>.
 */

Document::Document (std::string pathname, DocumentType type, int id)
	: _pathname (pathname),
	  _type (type),
	  _num_trigrams (0),
	  _group_id (id)
{}

Document::Document (Document * document)
	: _pathname (document->_pathname),
	  _type (document->_type),
	  _num_trigrams (0),
	  _group_id (document->_group_id)
{}

void Document::SetType (DocumentType type)
{
	_type = type;
}

bool Document::IsTextType () const
{
	return (_type == TypeText);
}

std::string Document::GetPathname () const 
{
	return _pathname;
}

void Document::SetPathname (std::string pathname)
{
	_pathname = pathname;
}

int Document::GetGroupId () const
{
	return _group_id;
}

void Document::SetGroupId (int id) 
{
	_group_id = id;
}

int Document::GetTrigramCount () const
{
	return _num_trigrams;
}

// WARNING: This method should only be used when document definitions are 
// loaded from a file.
void Document::SetTrigramCount (int count)
{
	_num_trigrams = count;
}

void Document::ResetTrigramCount ()
{
	_num_trigrams = 0;
}

void Document::IncrementTrigramCount ()
{
	_num_trigrams += 1;
}

// Start input from the file referred to by this document
bool Document::StartInput (TokenSet & tokenset)
{
	_fb = new std::ifstream(GetPathname().c_str (), std::ifstream::in); 
	if (_fb->is_open ())
	{
		_cin = _fb;
		InitialiseInput (tokenset);
		return true;	// signify file opened correctly
	}
	else
	{
		return false;
	}
}

// Start input from a provided input stream
bool Document::StartInput (std::istream & input, TokenSet & tokenset)
{
	_cin = &input;
	InitialiseInput (tokenset);
	return true;
}

// Start input by constructing a new Reader based on current document type
// TokenSet is provided by caller, so Reader uses common set of labels for tokens
void Document::InitialiseInput (TokenSet & tokenset)
{
	if (_type == TypeText)
	{
		_token_input = new WordReader (* _cin);
	}
	else // (_type == typeCode)
	{
		_token_input = new CCodeReader (* _cin);
	}
	ReadTrigram (tokenset); // read first two tokens so next call to 
	ReadTrigram (tokenset); // ReadTrigram returns the first complete trigram
}

// returns true if this document's filetype is the same as the given extension
// -- note, case is ignored, so "txt" == "TXT" == "tXt"
bool Document::IsFileType (std::string extension) const
{
	int dot_posn = _pathname.find_last_of ('.', true); // search for last dot, i.e. from end
	if (dot_posn == _pathname.npos) return false;
	std::string file_extension = _pathname.substr (dot_posn+1, _pathname.npos-(dot_posn+1));

	return StringToUpper(file_extension) == StringToUpper(extension); // ignore case in comparison
}

std::string Document::StringToUpper (std::string str) const
{
  std::string nstr = "";

  for(int i=0, l = str.length (); i < l; i += 1)
  {
    nstr += std::toupper(str[i]);
  }
  return nstr;
}

// Test if file extension represents a c-type language
bool Document::IsCodeType () const
{
	return  IsFileType ("cpp") ||
		IsFileType ("c") ||
		IsFileType ("java") ||
		IsFileType ("h");
}

// Test if file extension represents a pure text document
bool Document::IsTxtType () const
{
	return IsFileType ("txt");
}

// Test if file is not a known type
bool Document::IsUnknownType () const
{
	return ! (IsCodeType () || IsTxtType ());
}

// Reads next input token and updates information held on current trigram.
// return true if a trigram has been read and is ready for retrieval
bool Document::ReadTrigram (TokenSet & tokenset)
{
	_current_tuple[0] = _current_tuple[1];
	_current_tuple[1] = _current_tuple[2];
	_current_start[0] = _current_start[1];
	_current_start[1] = _current_start[2];
	if ( _token_input->ReadToken () )
	{
		_current_tuple[2] = _token_input->GetToken (tokenset);
		_current_start[2] = _token_input->GetTokenStart ();
		return true;
	}
	else 
	{
		return false;
	}
	
}

// retrieve a token of the current tuple, based on position within tuple
// -- as we only deal with trigrams, index must be in [0,2]
std::size_t Document::GetToken (int i) const
{
	assert (i>=0 && i<=2);
	return _current_tuple[i];
}

// retrieve the start position of current trigram
std::size_t Document::GetTrigramStart () const
{
	return _current_start[0];
}

// retrieve the start position of token i within current trigram
// -- used to get start position of second word
std::size_t Document::GetTrigramStart (int i) const
{
	assert (i>=0 && i<=2);
	return _current_start[i];
}

// retrieve the end position of the current token
std::size_t Document::GetTrigramEnd () const
{
	return _token_input->GetTokenEnd ();
}

// Close up the input file buffer
void Document::CloseInput ()
{
	delete _token_input;
	if (_fb->is_open ())
	{
		_fb->close ();
	}
}

